#include "servidor.h"
#include <QStringList>

Servidor::Servidor(QObject *parent) : QObject(parent)
{
    this->servidor = new QTcpServer();
    connect(this->servidor, SIGNAL(newConnection()), this, SLOT(novaConexao()));
    this->servidor->listen(QHostAddress::Any, 20000);
}

void Servidor::novaConexao()
{
    this->conexoes.append(this->servidor->nextPendingConnection());
    this->dados_peers.append(QByteArray());
    connect (this->conexoes.at(this->conexoes.size() - 1), SIGNAL(readyRead()), this, SLOT(dadosRecebidos()));
    connect (this->conexoes.at(this->conexoes.size() - 1), SIGNAL(error(QAbstractSocket::SocketError)),
             this, SLOT(erro(QAbstractSocket::SocketError)));
}

void Servidor::erro(QAbstractSocket::SocketError error)
{
    qDebug () << error;
    if (error == QAbstractSocket::RemoteHostClosedError) {

        if (this->conexoes.size() == 1) {
            this->arquivos_disponiveis.clear();
            this->conexoes.clear();
            this->dados_peers.clear();
            return;
        }

        int i;
        for (i = 0; i < this->conexoes.size() && this->conexoes.at(i) != this->sender(); i++) {}
        if (i == this->conexoes.size())
            return;

        for (int j = 0; j < this->arquivos_disponiveis.size(); j++) {
            if (this->arquivos_disponiveis.at(j)->getLocalizacao().contains(this->conexoes.at(i)->peerAddress().toString())) {
                this->arquivos_disponiveis.at(j)->removerLocalizacao(this->conexoes.at(i)->peerAddress().toString());
            }
        }

        QByteArray* dados = new QByteArray();
        dados->append(QByteArray("#remover#"));
        dados->append(this->conexoes.at(i)->peerAddress().toString());
        dados->append("#fim#");

        this->conexoes.removeAt(i);
        this->dados_peers.remove(i);

        for (int i = 0; i < this->conexoes.size(); i++) {
            this->conexoes.at(i)->write(*dados);
        }

        for (int i = 0; i < this->arquivos_disponiveis.size(); i++) {
            if (this->arquivos_disponiveis.at(i)->getLocalizacao().size() == 0) {
                this->arquivos_disponiveis.remove(i);
                i--;
            }
        }
    }
}

void Servidor::dadosRecebidos()
{
    for (int i = 0; i < this->conexoes.size(); i++) {
        if (this->sender() == this->conexoes.at(i)) {
            this->dados_peers[i].push_back(this->conexoes.at(i)->readAll());
            if (this->dados_peers.at(i).contains(QByteArray("#fim#"))) {
                if (this->dados_peers.at(i).contains("#lista#")) {
                    for (int j = 0; j < this->conexoes.size(); j++) {
                        if (i != j) {
                            this->conexoes.at(j)->write(this->dados_peers.at(i));
                        }
                        else {
                            this->conexoes.at(j)->write(*(this->gerarListaServidor()));
                        }
                    }
                    this->processarLista(i);
                }
                else if (this->dados_peers.at(i).contains("#arquivo#")) {
                    for (int j = 0; j < this->conexoes.size(); j++)
                        if (i != j)
                            this->conexoes.at(j)->write(this->dados_peers.at(i));
                    this->adicionarNovoArquivo(i);
                }
            }
            break;
        }
    }
}

void Servidor::processarLista(int posicao_lista)
{
    QString dados(this->dados_peers.at(posicao_lista));
    this->dados_peers[posicao_lista].clear();
    dados.replace("#fim#", "");
    dados.replace("#lista#", "");

    QStringList itens_lista = dados.split("#");

    QList<FileItem*> arquivos_novos;
    for (int i = 0; i < itens_lista.size(); i++) {
        FileItem* item = new FileItem();
        item->setNomeArquivo(itens_lista.at(i).split("|").at(0));
        item->setTamanho(itens_lista.at(i).split("|").at(1).toLongLong());
        item->adicionarLocalizacao(itens_lista.at(i).split("|").at(2));
        arquivos_novos.append(item);
    }

    for (int i = 0; i < arquivos_novos.size(); i++) {
        int j;
        for (j = 0; j < this->arquivos_disponiveis.size(); j++) {
            if (this->arquivos_disponiveis[j]->getNome() == arquivos_novos.at(i)->getNome() &&
                this->arquivos_disponiveis[j]->getTamanho() == arquivos_novos.at(i)->getTamanho()) {
                this->arquivos_disponiveis[j]->adicionarLocalizacao(arquivos_novos.at(i)->getLocalizacao().at(0));
                break;
            }
        }
        if (j == this->arquivos_disponiveis.size()) {
            this->arquivos_disponiveis.append(arquivos_novos.at(i));
        }
    }
}

QByteArray* Servidor::gerarListaServidor()
{
    QByteArray* dados = new QByteArray();
    dados->append(QByteArray("#lista#"));

    for (int i = 0; i < this->arquivos_disponiveis.size(); i++) {

        QString dados_arquivo;
        dados_arquivo.append(this->arquivos_disponiveis.at(i)->getNome() + "|");
        dados_arquivo.append(QString::number(this->arquivos_disponiveis.at(i)->getTamanho()) + "|");
        for (int j = 0; j < this->arquivos_disponiveis.at(i)->getLocalizacao().size(); j++)
            dados_arquivo.append(this->arquivos_disponiveis.at(i)->getLocalizacao().at(j) + "|");
        dados_arquivo.remove(dados_arquivo.size() - 1, 1);
        if (i < this->arquivos_disponiveis.size() - 1)
            dados_arquivo.append("#");
        dados->append(dados_arquivo);
    }
    dados->append("#fim#");
    return dados;
}

void Servidor::adicionarNovoArquivo(int i)
{
    QString dados(this->dados_peers.at(i));

    this->dados_peers[i].clear();

    dados.replace("#fim#", "");
    dados.replace("#arquivo#", "");

    if (dados == "")
        return;

    QStringList dados_arquivo = dados.split("|");

    if (dados_arquivo.size() != 3)
        return;

    FileItem* item = new FileItem();
    item->setNomeArquivo(dados_arquivo.at(0));
    item->setTamanho(dados_arquivo.at(1).toLongLong());
    item->adicionarLocalizacao(dados_arquivo.at(2));

    bool inserted = false;

    for (int i = 0; i < this->arquivos_disponiveis.size() && !inserted; i++) {
        if (this->arquivos_disponiveis.at(i)->getNome() == item->getNome() &&
            this->arquivos_disponiveis.at(i)->getTamanho() == item->getTamanho()) {
            this->arquivos_disponiveis.at(i)->adicionarLocalizacao(item->getLocalizacao().at(0));
            inserted = true;
        }
    }

    if (!inserted) {
        this->arquivos_disponiveis.append(item);
    }
}
